
/* #######################################################
   #################TRABALHO DE ICC2######################
   #######################################################

Autores: -> Matheus Amato Colussi - Número USP: 6792242
         -> Rafael Mira - Número USP: 4197025

   #######################################################*/

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

typedef struct busca
{
    float salario;
    char nome[63];
}RegBusca;

typedef struct Registro_Pessoa
{
    char nome[64];
    int idade;
    float salario;
    char sexo;
}Registro;

typedef enum {nome, salario} Opcao;

/*Funcao que irá ler do arquivo fornecido as informações necessárias
Entrada:
        P1: Ponteiro para ponteiro do tipo Registro (para mudar o endereço para apontar o vetor com as structs)
        P2: Ponteiro para ponteiro do tipo RegBusca (para mudar o endereço para apontar o vetor com as chaves de buscas)
        P3: String com o caminho do arquivo que será lido
        P4: Ponteiro para o número total de registros
        P5: Ponteiro para o número total de chaves de busca
        P6: Ponteiro para a opção desejada (ref enum Opcao
        P7: Ponteiro para flag de erro
Saída: Endereço do vetor com os registros e endereço do vetor com as chaves de busca, além dos valores alterados por ponteiro de nregistros, nbuscas, e da opção desejada
*/

void acaoLerArquivo(Registro **vetorRegistro, RegBusca **vetorBusca, char caminho[],int *nregistros, int *nbuscas, Opcao *opcao, int *erro)
{
    int i;
    FILE *arq;
    char buffercaminho[500]; /*Variavel que recebera o caminho para ser passado para o fopen*/
    Registro *vetorRegistroAUX; /* Ponteiros auxiliares para passar depois o ponteiro destes para fora da funçăo */
    RegBusca *vetorBuscaAUX;
    sprintf(buffercaminho, "%s", caminho);
    arq = fopen(buffercaminho, "r");
    if (!arq)
    {
        *erro = 1;
    }
    else
    {
        fscanf(arq,"%d", nregistros);

        vetorRegistroAUX = (Registro*)malloc(*nregistros*sizeof(Registro));

        for (i=0;i<*nregistros;i++)
        {
            fscanf(arq,"\n%[^;];%d;%f;%c",&vetorRegistroAUX[i].nome,&vetorRegistroAUX[i].idade,&vetorRegistroAUX[i].salario,&vetorRegistroAUX[i].sexo);

        }

        fscanf(arq,"%d %d", nbuscas, opcao );

        vetorBuscaAUX = (RegBusca*)malloc(*nbuscas*sizeof(RegBusca));

        for (i=0;i<*nbuscas;i++)
        {
            if (*opcao == nome)
            {
                fscanf(arq,"\n%[^\n]", vetorBuscaAUX[i].nome);
            }
            else if (*opcao == salario)
            {
                fscanf(arq,"\n%f", &vetorBuscaAUX[i].salario);
            }
            *erro=0;
        }
    }
    fclose(arq);
    *vetorRegistro = vetorRegistroAUX; /*Passa o ponteiro vetorRegistroAUX para o ponteiro de ponteiro vetorRegistro (para passar os vetores para fora da funçăo */
    *vetorBusca = vetorBuscaAUX; /*Passa o ponteiro vetorBuscaAUX para o ponteiro de ponteiro vetorBusca (para passar os vetores para fora da funçăo */
}

/*Funcao para escrever o registro no arquivo
Entrada:
        P1: Ponteiro para o vetor de Registros
        P2: Posicao do registro a ser escrito no vetor
        P3: Número de registros para ser usado no nome do arquivo de escrita
        P4: Opcao desejada, também para ser usada no nome do arquivo a ser escrito
        P5: O tipo de busca a ser feita - sequencial(0) ou binária(1) *Não está em ENUM
        P6: Flag para saber se achou ou não o registro, caso não ache ele imprime nao encontrado
        P7: Ponteiro para a flag erro
Saída: Ele escreve no arquivo o registro ou a string "nao encontrado"
*/
void acaoEscreveArquivoRegistro(Registro *vetorRegistro, int posicao, int nregistros,Opcao opcao, int tipobusca, int achou, int *erro)
{
    FILE *arqescrita;

    char buffercaminho[500]; /*Cria o buffer para o nome do arquivo que sera escrito */
    switch (tipobusca) /*Verifica qual o tipo de busca para nomear o arquivo, se for sequencial(0) ou binaria(1) */
    {
    case 0:/*Simplesmente para adequar o caminho do arquivo que será escrito */
        if (opcao == nome)
        {
            sprintf(buffercaminho, "sequencial_nomes_%d.txt", nregistros);/* nregistros pois o nome do arquivo tem a ver com o numero de registros que ele possui */
            break;
        }
        else
        {
            sprintf(buffercaminho, "sequencial_salarios_%d.txt", nregistros);/* nregistros pois o nome do arquivo tem a ver com o numero de registros que ele possui */
            break;
        }

    case 1:
        if (opcao == nome)
        {
            sprintf(buffercaminho, "binaria_nomes_%d.txt", nregistros);
            break;
        }
        else
        {
            sprintf(buffercaminho, "binaria_salarios_%d.txt", nregistros);
            break;
        }
    }
    /*Abre o arquivo */
    arqescrita = fopen(buffercaminho, "a");
    if (!arqescrita) *erro = 1;
    /*Se foi passado como parametro achou = 0, ele imprime nao encontrado, caso contrario ele imprime a struct */
    else
    {
        if (!achou) fprintf(arqescrita, "nao encontrado\n");
        else
        {
            fprintf(arqescrita, "%s;%d;%.2f;%c\n", vetorRegistro[posicao].nome, vetorRegistro[posicao].idade, vetorRegistro[posicao].salario, vetorRegistro[posicao].sexo);
        }
        *erro = 0;
    }

    fclose(arqescrita);
}

/*Funcao para escrever a média de comparações no arquivo
Entrada:
        P1: Ponteiro para a média a ser escrita no arquivo
        P2: Número de registros (para o nome do arquivo)
        P3: Enum da opcao, também para o nome do arquivo de escrita
        P4: O tipo da busca, se é sequencial ou binaria (0,1)
        P5: Ponteiro para a flag de erro
Saída: é escrito o número médio de comparações no arquivo.
*/
void acaoEscreveArquivoComparacoes(float media, int nregistros, Opcao opcao, int tipobusca, int *erro)
{
    FILE *arqescrita;

    char buffercaminho[500]; /*Cria o buffer para o nome do arquivo que sera escrito */
    switch (tipobusca) /*Verifica qual o tipo de busca para nomear o arquivo, se for sequencial(0) ou binaria(1) */
    {
    case 0:/*Simplesmente para adequar o caminho do arquivo que será escrito */
        if (opcao == nome)
        {
            sprintf(buffercaminho, "sequencial_nomes_%d.txt", nregistros);/* nregistros pois o nome do arquivo tem a ver com o numero de registros que ele possui */
            break;
        }
        else
        {
            sprintf(buffercaminho, "sequencial_salarios_%d.txt", nregistros);
            break;
        }

    case 1:
        if (opcao == nome)
        {
            sprintf(buffercaminho, "binaria_nomes_%d.txt", nregistros);
            break;
        }
        else
        {
            sprintf(buffercaminho, "binaria_salarios_%d.txt", nregistros);
            break;
        }
    }

    arqescrita = fopen(buffercaminho, "a");
    if (!arqescrita) *erro = 1; /*Verifica se deu algum erro na hora de abrir o arquivo*/
    else
    {
        fprintf(arqescrita,"%.1f", media);
        *erro = 0;
    }
    fclose(arqescrita);
}

/*Funcao para realizar a busca binária
Entrada:
        P1: Ponteiro para um vetor do tipo Registro (vetor aonde será pesquisado os termos do vetor RegBusca)
        P2: Ponteiro para um vetor do tipo RegBusca (vetor que irá conter as chaves de buscas)
        P3: Posição da chave de busca no vetorBusca
        P4: Numero total de registros (argumento para usar na chamada a funcao acaoEscreveRegistro)
        P5: Opção do arquivo: nome(0) ou salário(1)
        P6: Inteiro para o inicio do vetor (ou subvetor) que será pesquisado
        P7: Inteiro para o fim do vetor (ou subvetor) que será pesquisado
        P8: Ponteiro para a variavel contadora ncomparacoes (que é float, pois sera dividida para escrever a média de comparações)
        P9: Flag BOOLEANA para saber se achou ou não (será passada para a função acaoEscreveRegistro)
        P10: Ponteiro para uma flag BOOLEANA de erro (1 caso tenha algum erro, 0, caso contrário)
Saída: 1 se achou, 0, caso contrário
         */

void acaoBuscaBinaria(Registro *vetorRegistro, RegBusca *vetorBusca, int nregistros, int nbuscas, Opcao opcao, int *erro)
{
    int i,ini,fim, k;
    int achou;
    float ncomparacoes = 0;


    for (i=0;i<=nbuscas;i++)
    {
        achou = 0;
        ini = 0;
        fim = nregistros;
        while (ini<=fim)
        {
            k=(fim+ini)/2;
            if (opcao == nome) /* Caso a opcao passada seja o nome ele entra nesse if para fazer operacoes com strcmp */
            {
                ncomparacoes++; /* Incrementará a variável pois vai se comparar os dados pela primeira vez*/
                if (strcmp(vetorBusca[i].nome, vetorRegistro[k].nome) == 0) /*Verifica se o nome na posicao k do vetor é igual a chave de busca */
                {
                    achou = 1;
                    break;
                }

                else if (strcmp(vetorBusca[i].nome, vetorRegistro[k].nome) > 0) /*Caso o chave de busca seja alfabeticamente maior que o nome na posição k, ele dividi o vetor e busca na segunda metade */
                {
                    ncomparacoes++;
                    ini=k+1;
                    achou=0;
                }
                else {
                      ncomparacoes++;
                      fim=k-1;
                      achou=0;
                     }
            }
            else if (opcao == salario)
            {
                ncomparacoes++; /* Incrementará a variável pois vai se comparar os dados pela primeira vez*/
                if (vetorBusca[i].salario == vetorRegistro[k].salario) /*Caso encontre ele entra aqui */
                {
                    achou = 1;
                    break;
                }

                if (vetorBusca[i].salario > vetorRegistro[k].salario) /*Caso a chave de busca seja maior, usa o subvetor acima de k*/
                {
                    ncomparacoes++;
                    ini=k+1;
                    achou=0;
                }
                else {
                      ncomparacoes++;
                      fim=k-1;
                      achou=0;
                     }

            }
        }


        acaoEscreveArquivoRegistro(vetorRegistro, k, nregistros, opcao, 1, achou, erro);
    }

    ncomparacoes = ncomparacoes/nbuscas;/*Faz a media de ncomparacoes para ser escrita no arquivo*/
    acaoEscreveArquivoComparacoes(ncomparacoes, nregistros, opcao, 1, erro);
}


int main()
{
    char caminho[500]; /*Variavel para ser inserido o caminho do arquivo*/
    int nregistros, nbuscas, erro, fim = 0;/*Variaveis para serem utilizadas no programa, sendo a ultima delas uma flag para saber se terminou o programa ou nao */
    Opcao opcao;
    Registro *vetorRegistro; /*Declaracao do ponteiro para o vetorRegistro que sera alocado dinamicamente mais tarde */
    RegBusca *vetorBusca; /*Declaracao do ponteiro para o vetorRegistro que sera alocado dinamicamente mais tarde */
    while (!fim) /*Enquanto a flag for fim = 0, continua executando */
    {

        gets(caminho); /*Pega o caminho dos arquivos */
        if (strcmp(caminho,"FIM")== 0) /*Compara o caminho com a palavra FIM, para verificar se termina ou nao o programa */
        {
            fim = 1;/*Em caso afirmativo, muda a flag */
            continue; /*E pula o resto do codigo ate o while, poderia ser usado o break, mas assim fica mais intuitivo */
        }

        acaoLerArquivo(&vetorRegistro, &vetorBusca, caminho, &nregistros, &nbuscas, &opcao, &erro); /*Funcao para ler o arquivo e retornar duas structs, alem de varias variaveis */
        acaoBuscaBinaria(vetorRegistro, vetorBusca, nregistros, nbuscas, opcao, &erro); /*Funcao que fara a busca binária*/
        free(vetorBusca);/*Limpa a memoria das variaveis vetorBusca e vetorRegistro*/
        free(vetorRegistro);
    }
    return 0;
}


